/* vim: set sw=8 ts=8 sts=8 expandtab: */
#include "Cmaid.h"
#include "cmaid_private.h"

CMAID_INLINE void *
cmaid_node_value_assign(void *pos, const Cmaid_Value *v, const void *data,
                size_t size)
{
        if (size)
        {
                if (v->sized.copy)
                        v->sized.copy(pos, data, size);
                else
                        memcpy(pos, data, size);

                return pos;
        }
        else if (v && v->pointer.dup)
                return v->pointer.dup(data);
        
        return (void *)data;
}

static unsigned int
cmaid_node_value_size_calculate(const Cmaid_Value *v, const void *val,
                unsigned int val_size)
{
        size_t data_size = 0;

        if (v)
        {
                if (v->size > 0)
                        data_size = v->size;
                else if (v->size < 0)
                {
                        if (val_size)
                                data_size = val_size;
                        else
                                data_size = v->sized.size(val);
                }
        }

        return data_size;
}

void *
cmaid_single_node_new(unsigned int node_size, const Cmaid_Value *v,
                const void *val, unsigned int val_size, void **ptr_val)
{
        unsigned char *node; /* note: this is not a string */
        size_t data_size = 0;

        data_size = cmaid_node_value_size_calculate(v, val, val_size);
        node = MALLOC(node_size + data_size);
        *ptr_val = cmaid_node_value_assign(node + node_size, v, val, data_size);

        return node;        
}

void *
cmaid_single_node_free(void *node, const Cmaid_Value *key_info,        void *key)
{
        void *ret = NULL;

        if (key_info->size == 0)
        {
                if (key_info->pointer.free)
                        key_info->pointer.free(key);
                else
                        ret = key;
        }
        else if (key_info->sized.free)
                key_info->sized.free(key);

        FREE(node);

        return ret;
}

void *
cmaid_pair_node_new(unsigned int node_size, const Cmaid_Value *key_info,
                const void *key, unsigned int key_size, void **ptr_key,
                const Cmaid_Value *val_info, const void *val,
                unsigned int val_size, void **ptr_val)
{
        size_t key_data_size;
        size_t val_data_size;
        unsigned char *node; /* note: this is not a string */

        key_data_size = cmaid_node_value_size_calculate(key_info, key,
                        key_size);
        val_data_size = cmaid_node_value_size_calculate(val_info, val,
                        val_size);

        node = MALLOC(node_size + key_data_size + val_data_size);

        /* set the key portion to the node */
        *ptr_key = cmaid_node_value_assign(node + node_size, key_info, key,
                                                key_data_size);
        /* set the val portion to the node */
        *ptr_val = cmaid_node_value_assign(node + node_size + key_data_size,
                                                val_info, val, val_data_size);

        return node;        
}

void *
cmaid_pair_node_renew(void *node, unsigned int node_size, unsigned int key_size,
                                const Cmaid_Value *val_info, const void *val, 
                                void **ptr_val)
{
        unsigned int val_data_size;
        unsigned char *n = node;

        /* get the new size of the value data */
        if (val_info->size >= 0)
                val_data_size = val_info->size;
        else
        {
                val_data_size = val_info->sized.size(val);
                n = REALLOC(n, node_size + key_size + val_data_size);
        }
        
        /* set the val portion to the node */
        *ptr_val = cmaid_node_value_assign(n + node_size + key_size, val_info,
                                                val, val_data_size);

        return n;
}

void *
cmaid_pair_node_free(void *node, const Cmaid_Value *key_info, void *key,
                        const Cmaid_Value *val_info, void *val)
{
        void *ret = NULL;

        if (key_info->size == 0 && key_info->pointer.free)
                key_info->pointer.free(key);
        else if (key_info->sized.free)
                key_info->sized.free(key);
        
        if (val_info->size == 0)
        {
                if (val_info->pointer.free)
                        val_info->pointer.free(val);
                else
                        ret = val;
        }
        else if (val_info->sized.free)
                val_info->sized.free(val);

        FREE(node);

        return ret;
}
